use actix_web::{body::BoxBody, http::header::ContentType, HttpResponse, Responder, ResponseError};
use serde::Serialize;
use std::borrow::Cow;

use crate::error::AppErr;

pub const RESP_HEAD: &'static str = "resp";
pub const RESP_OK: &'static str = "ok";
pub const RESP_FAIL: &'static str = "fail";

#[derive(Debug, Serialize)]
pub struct ErrResp {
    pub err_code: i32,
    pub err_msg: Cow<'static, str>,
}

impl ErrResp {
    pub fn new(ec: i32, msg: Cow<'static, str>) -> ErrResp {
        ErrResp {
            err_code: ec,
            err_msg: msg,
        }
    }
}

impl ResponseError for AppErr {
    fn error_response(&self) -> HttpResponse {
        let resp = ErrResp::new(-1, Cow::Owned(self.to_string()));
        HttpResponse::Ok()
            .content_type(ContentType::octet_stream())
            .insert_header((RESP_HEAD, RESP_FAIL))
            .body(serde_cbor::to_vec(&resp).unwrap())
    }
}

pub struct BodyResp<T> {
    pub body: T,
}

impl<T: Serialize> Responder for BodyResp<T> {
    type Body = BoxBody;

    fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse {
        HttpResponse::Ok()
            .content_type(ContentType::octet_stream())
            .insert_header((RESP_HEAD, RESP_OK))
            .body(serde_cbor::to_vec(&self.body).unwrap())
    }
}

pub struct NoneResp {}

impl Responder for NoneResp {
    type Body = BoxBody;

    fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse {
        HttpResponse::Ok()
            .content_type(ContentType::octet_stream())
            .insert_header((RESP_HEAD, RESP_OK))
            .finish()
    }
}

pub type BodyResult<T> = Result<BodyResp<T>, AppErr>;
pub type VoidResult = Result<NoneResp, AppErr>;

pub fn new_ok<T>(body: T) -> BodyResult<T> {
    Ok(BodyResp { body })
}

pub fn new_void() -> VoidResult {
    Ok(NoneResp {})
}
